2. Multi-Tenant Modes

The webapp always runs in multi-tenant mode, but you have the choice on how your database will operate. You can choose either one database per customer (single-tenant), or one database for all customers (multi-tenant).

In Config.groovy, add the following section:

tenant {
    mode = "singleTenant" // OR "multiTenant"
}

multiTenant is the default

2.1 Multi-Tenant Database Set Up

The 'multiTenant' mode relies your application to use just one database for all tenants running. This is useful when you have a SaaS that allows user to sign up online and have their tenants running just after signing up. This is the simpler approach on multi-tenancy.

First thing to do, is to tell the plugin witch classes will be treated as multi-tenant ones. You can do it, but annotating your domain classes with the @MultiTenant annotation, as show below:

import grails.plugin.multitenant.core.groovy.compiler.MultiTenant

@MultiTenant class MyDomainClass {

}

This way, each instance of MyDomainClass will belong to some tenant. To achieve this, the plugin hooks into all hibernate events that query your database, adding another condition in your queries that will filter just instances of the current tenant.

For example, imagine you have 2 tenants, each one with 2 instances of MyDomainClass. When the app is running on tenant 1, calling

def lst = MyDomainClass.list()

will return just its 2 instances, even if you have 4 in your database.

The plugin hooks either on save/update events, setting the value of tenantId property automagically.

You don't need to declare the 'tenantId' property, the plugin even auto-inject it for you, so, keep your domain class code clean

2.1.1 Multi-Tenant mode gotchas

A note on indexing in multi-tenant environment

Indexing a multi-tenant database is a tricky beast. If you have large datasets (100MM-1BB), prepare yourself for some fun. Google 'multi-tenant databases' and start reading. :)

The short of it is that it's probably most efficient to create multi-column indexes that always include tenantId, that way the db can limit the working set to a single tenant before it starts filtering on another field.

2.2 Single-Tenant Database Set Up

The single-tenant option is a little more immature than the multi-tenant option. It inherits all datasource configuration from the default datasource in Datasource.groovy, and allows you to provide a custom datasource url per tenant. This means that you can't mix and match drivers or user/pass combos for different tenants in the same instance.

TenantUtils.doWithTenant and currentTenant.set() do not work in single tenant mode, unless you manually create a new hibernate session and bind it to the current thread.

Also, normal second-level caches will not work in single-tenant mode, as you will get primary key collisions across databases. You'll need to use a multi-tenant wrapper for your cache implementation (see below)

The plugin supports the following types of datasources:

Resolving the DataSource

Note: Everywhere in the documentation that you see a datasource url, you can also use a jndiName. If the system encounters a tenant you haven't mapped, it will use the default datasource configuration in Datasource.groovy. This includes all database operations that happen before grails is completely loaded.

Mapping DataSources through Config.groovy

To map datasource urls in Config.groovy, add the following code:

tenant {
    mode = "singleTenant"
    datasourceResolver.type = "config" //This is the default and can be omitted
    dataSourceTenantMap {

//The "t" before the tenantId is required because you can't have a //variable that's a number t1 = "jdbc:mysql://localhost/ets_dev1" t2 = "jdbc:mysql://localhost/ets_dev2" t3 = "jdbc:mysql://localhost/ets_dev3"

//JNDI Example t1 = "java:comp/env/myDataSource" } }

Mapping DataSources through the database

Mapping data sources through the database allows them to be provisioned on the fly without any restarting or reloading.

Add the following line to Config.groovy

tenant {
    mode = "singleTenant"
    datasourceResolver.type = "db" 
}

Then run the following command:

grails create-data-source-map

This will create a domain class used to store the mappings (tenant.DataSourceTenantMap). You can add a new mapping on the fly like this:

def dsMap = new DataSourceTenantMap()
dsMap.mappedTenantId = 5
dsMap.dataSource = "jdbc:mysql://myserver/customerdb"

//Jndi Example //dsMap.dataSource = "java:comp/env/myDataSource" dsMap.save()

When the record is saved, the datasource resolver will be automatically updated, and the app can start accessing the new datasource immediately

2nd-Level Caching

As mentioned above, regular 2nd level caches will not work in single-tenant mode, as you will get primary key collisions across databases. You will need to use a special wrapped version of the cache that makes it tenant-aware. The wrapped versions should maintain all your settings, but each tenant will maintain its OWN cache instances. This means that if can't simply set cache settings to be applied to the entire JVM; you'll have to 'divide' the settings among tenants. To set this update your datasource.groovy file to set

cache.provider_class = 'grails.plugin.multitenant.oscache.cache.MultiTenantOSCacheProvider'

or

cache.provider_class = 'grails.plugin.multitenant.ehcache.cache.MultiTenantEhCacheProvider'

OSCache

If you use the OpenSymphony cache provider (org.hibernate.cache.OSCacheProvider), you should use grails.plugin.multitenant.oscache.cache.MultiTenantOSCacheProvider instead.

EHCache(New with 0.18)

If you are using the EHCache cache provider your should use grails.plugin.multitenant.ehcache.cache.MultiTenantEhCacheProvider insead.